﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Renci.SshNet;
using System.Threading;

namespace PurpleKnife.Library
{
    public class SwitchInfoReader
    {
        private pkSwitch Switch;
        private Thread readerThread;
        private SshClient sshCli;

        pkSwitchInfo infObj;

        public event EventHandler<ReadCompletedEventArgs> ReadCompleted;
        public event EventHandler<HostKeyUnknownEventArgs> HostKeyUnknown;

        public void Read(pkSwitch Switch)
        {
            readerThread = new Thread(ReadFromSwitch);
            readerThread.IsBackground = true;
            readerThread.Start(Switch);
        }
        
        private void ReadFromSwitch(object SwitchToRead)
        {
            Switch = (pkSwitch)SwitchToRead;
            infObj = new pkSwitchInfo();

            // connection information
            string host = Switch.SSHHost;
            int port = Switch.SSHPort;
            string user = Switch.SSHUserName;
            string pwd = Switch.SSHPassword.ToString();

            // create and setup SSH client object
            sshCli = new SshClient(host, port, user, pwd);
            sshCli.HostKeyReceived += new EventHandler<Renci.SshNet.Common.HostKeyEventArgs>(sshCli_HostKeyReceived);
            sshCli.ErrorOccurred += new EventHandler<Renci.SshNet.Common.ExceptionEventArgs>(sshCli_ErrorOccurred);
            sshCli.ConnectionInfo.Timeout = Switch.SSHConnectionTimeout;
            sshCli.ConnectionInfo.RetryAttempts = 1;

            // build connection
            sshCli.Connect();
            
            // execute command "show switch"
            SshCommand cmdObj = sshCli.CreateCommand("show switch");
            cmdObj.CommandTimeout = TimeSpan.FromSeconds(30);
            string cmdOutShowSwitch = cmdObj.Execute();

            // close connection
            sshCli.Disconnect();

            
            
            // parse result
            ParseInfo(cmdOutShowSwitch);

            // create completed event args
            ReadCompletedEventArgs ea = new ReadCompletedEventArgs();
            ea.InformationIsValid = true;
            ea.SwitchInformation = infObj;

            // raise completed event
            OnReadCompleted(this, ea);

        }

        private void ParseInfo(string CommandOutput)
        {
            // TODO: parse config info
            // primary.cfg       Created by ExtremeXOS version 12.6.2.10
            //                   234119 bytes saved on Tue Jul  2 11:10:49 2013


            if (CommandOutput != "")
            {
                string itemName;
                string itemValue;
                string[] lines = CommandOutput.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                foreach (string line in lines)
                {
                    if (line.Contains("Created by"))
                    {
                        // config name and EXOS version
                        string[] parts = line.Split('\t');
                        infObj.ConfigName = parts[0].Trim();
                        infObj.ConfigCreatedBy = parts[1].Trim();
                    }
                    else
                    {
                        if (line.Contains("bytes saved on"))
                        {
                            // config size and save date
                        }
                        else
                        {
                            // other "show switch" items
                            itemName = GetItemName(line).ToLower();
                            itemValue = GetItemValue(line);
                            switch (itemName)
                            {
                                case "sysname":
                                    infObj.SysName = itemValue;
                                    break;
                                case "syslocation":
                                    infObj.SysLocation = itemValue;
                                    break;
                                case "syscontact":
                                    infObj.SysContact = itemValue;
                                    break;
                                case "system mac":
                                    infObj.SystemMAC = itemValue;
                                    break;
                                case "system type":
                                    infObj.SystemType = itemValue;
                                    break;
                                case "syshealth check":
                                    infObj.SysHealthCheck = itemValue;
                                    break;
                                case "recovery mode":
                                    infObj.RecoveryMode = itemValue;
                                    break;
                                case "system watchdog":
                                    infObj.SystemWatchdog = itemValue;
                                    break;
                                case "current time":
                                    infObj.CurrentTime = itemValue;
                                    break;
                                case "timezone":
                                    infObj.Timezone = itemValue;
                                    break;
                                case "boot time":
                                    infObj.BootTime = itemValue;
                                    break;
                                case "boot count":
                                    infObj.BootCount = itemValue;
                                    break;
                                case "next reboot":
                                    infObj.NextReboot = itemValue;
                                    break;
                                case "system uptime":
                                    infObj.SystemUptime = itemValue;
                                    break;
                                case "current state":
                                    infObj.CurrentState = itemValue;
                                    break;
                                case "image selected":
                                    infObj.ImageSelected = itemValue;
                                    break;
                                case "image booted":
                                    infObj.ImageBooted = itemValue;
                                    break;
                                case "primary ver":
                                    infObj.PrimaryVersion = itemValue;
                                    break;
                                case "secondary ver":
                                    infObj.SecondaryVersion = itemValue;
                                    break;
                                case "config selected":
                                    infObj.ConfigSelected = itemValue;
                                    break;
                                case "config booted":
                                    infObj.ConfigBooted = itemValue;
                                    break;
                            }
                        }
                    }
                    

                }
            }
        }

        // get item name from line
        private string GetItemName(string Line)
        {
            int colPos = Line.IndexOf(":");
            if (colPos > 0)
            {
                return Line.Substring(0, colPos).Trim();
            }
            else
            {
                return Line.Trim();
            }
        }

        // get item value from line
        private string GetItemValue(string Line)
        {
            int colPos = Line.IndexOf(":");
            if (colPos > 0)
            {
                return Line.Substring(colPos + 1).Trim();
            }
            else
            {
                return "";
            }

        }

        // RaiseEvent helper
        protected virtual void OnReadCompleted(object sender, ReadCompletedEventArgs e)
        {
            // raise the event
            EventHandler<ReadCompletedEventArgs> handler = ReadCompleted;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        protected virtual void OnHostKeyUnknown(object sender, HostKeyUnknownEventArgs e)
        {
            // raise the event
            EventHandler<HostKeyUnknownEventArgs> handler = HostKeyUnknown;
            if (handler != null)
            {
                handler(this, e);
            }
        }


        public class ReadCompletedEventArgs : EventArgs
        {
            public bool InformationIsValid { set; get; }
            public pkSwitchInfo SwitchInformation { set; get; }
        }

        public class HostKeyUnknownEventArgs : EventArgs
        {
            public pkSwitch Switch { set; get; }
            public string HostKeyFingerprint { set; get; }
            public bool KeyIsTrusted { set; get; }
        }

        // handle SSH error
        private void sshCli_ErrorOccurred(object sender, Renci.SshNet.Common.ExceptionEventArgs e)
        {
            // TODO: implement
            ;
        }

        // compare SSH host key fingerprints
        private void sshCli_HostKeyReceived(object sender, Renci.SshNet.Common.HostKeyEventArgs e)
        {
            // debugging
            e.CanTrust = true;
            return;



            // fingerprint received
            string fingerprintReceived = BitConverter.ToString(e.FingerPrint);
            // fingerprint stored
            string fingerprintStored = Switch.SSHKeyFingerprint;

            if (fingerprintStored == fingerprintReceived)
            {
                e.CanTrust = true;
            }
            else
            {
                // create event args object
                HostKeyUnknownEventArgs ea = new HostKeyUnknownEventArgs();
                ea.Switch = Switch;
                ea.HostKeyFingerprint = fingerprintReceived;
                // raise event
                OnHostKeyUnknown(this, ea);
                // check if key is trusted
                e.CanTrust = ea.KeyIsTrusted;
            }
        }


        
    }
}
